package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.ModelSpecificationInfo;
import cn.mw.cmdb.entity.ModelVendorInfo;
import cn.mw.cmdb.param.SpecificationExcelImportParam;
import cn.mw.cmdb.param.SpecificationSearchParam;
import cn.mw.cmdb.param.VendorSearchParam;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.util.ExcelUtils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.google.common.base.Strings;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;

@Slf4j
public class SpecificationExcelImportListener extends AnalysisEventListener<SpecificationExcelImportParam> {

    private HttpServletResponse response;

    private CmdbServiceManage cmdbServiceManage;

    /**
     * 文件名称
     */
    private String fileName;

    /**
     * 操作人员名称
     */
    private String operatorName;

    /**
     * 每隔100条存储数据库，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 解析成功的数据
     */
    List<SpecificationExcelImportParam> list = new ArrayList<>();

    /**
     * 解析失败的数据
     */
    List<SpecificationExcelImportParam> failList;

    public SpecificationExcelImportListener(HttpServletResponse response, String fileName, String operatorName, CmdbServiceManage cmdbServiceManage,List<SpecificationExcelImportParam> failList) {
        this.response = response;
        this.fileName = fileName;
        this.operatorName = operatorName;
        this.cmdbServiceManage = cmdbServiceManage;
        this.failList = failList;
    }

    /**
     * When analysis one row trigger invoke function.
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @SneakyThrows
    @Override
    public void invoke(SpecificationExcelImportParam data, AnalysisContext context) {
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    /**
     * if have something to do after all analysis
     *
     * @param context
     */
    @SneakyThrows
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (list.size() > 0) {
            saveData();
            log.info("所有数据解析完成！");
        }
        if (failList.size() > 0) {
            excelFailData(failList);
            log.info("解析失败的所数据导出完成");
        }
    }

    /**
     * 导出错误数据至excel文件里
     *
     * @param failList 错误数据
     */
    private void excelFailData(List<SpecificationExcelImportParam> failList) {
        Set<String> includeColumnFiledNames = new HashSet<>();
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getSpecification));
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getVendor));
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getType));
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getDeviceHeight));
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getDescription));
        includeColumnFiledNames.add(CMDBTool.getFieldName(SpecificationExcelImportParam::getErrorMsg));

        ExcelWriter excelWriter = null;
        String failFileName = fileName.indexOf(".") != -1 ? fileName.substring(0, fileName.indexOf(".")) : fileName;
        failFileName = "error_" + failFileName;
        try {
            excelWriter = ExcelUtils.getExcelWriter(failFileName, response, SpecificationExcelImportParam.class);
            WriteSheet sheet = EasyExcel.writerSheet(0, "sheet" + 0)
                    .includeColumnFiledNames(includeColumnFiledNames)
                    .build();
            excelWriter.write(failList, sheet);
            log.info("导出成功");
        } catch (IOException e) {
            log.error("导出失败{}", e);
        } finally {
            if (null != excelWriter) {
                excelWriter.finish();
            }
        }
    }

    /**
     * 保存数据
     */
    private void saveData() throws Exception {
        List<ModelSpecificationInfo> specInfos = new ArrayList<>();
        //**** 1:获取所有校验数据 ****

        //获取所有厂商信息
        ModelVendorServiceImpl vendorService = (ModelVendorServiceImpl) cmdbServiceManage.getModelService(ModelVendorServiceImpl.ID);
        List<ModelVendorInfo> vendorInfoList = vendorService.doSelectList(new VendorSearchParam());
        Map<String, String> vendorMap = vendorInfoList.stream().filter(s -> !Strings.isNullOrEmpty(s.getVendorCN())).collect(Collectors.toMap(s -> s.getVendorCN().toLowerCase(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));

        //获取所有规格信号信息
        ModelSpecificationServiceImpl specService = (ModelSpecificationServiceImpl) cmdbServiceManage.getModelService(ModelSpecificationServiceImpl.ID);
        List<ModelSpecificationInfo> specInfoList = specService.doSelectList(new SpecificationSearchParam());
        //以厂商id+规格型号为key
        Map<String, String> specMap = specInfoList.stream().filter(s -> !Strings.isNullOrEmpty(s.getVendorId()) && !Strings.isNullOrEmpty(s.getSpecification())).collect(Collectors.toMap(s -> s.getSpecification().toLowerCase() + s.getVendorId(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));

        List<ModelSpecificationInfo> specList = new ArrayList<>();
        //**** 2:数据校验 ****
        log.info("{}条数据，开始添加模板数据！", list.size());
        if (list.size() > 0) {
            for (SpecificationExcelImportParam importParam : list) {
                ModelSpecificationInfo modelSpecificationInfo = new ModelSpecificationInfo();
                modelSpecificationInfo = JSONObject.parseObject(JSONObject.toJSONString(importParam), ModelSpecificationInfo.class);
                StringBuffer sbf = new StringBuffer();
                if (vendorMap != null && vendorMap.containsKey(strValueConvert(importParam.getVendor()).toLowerCase())) {
                    modelSpecificationInfo.setVendorId(vendorMap.get(strValueConvert(importParam.getVendor()).toLowerCase()));
                } else {
                    sbf.append("厂商名称不存在");
                    sbf.append(";");
                }
                String key = strValueConvert(importParam.getSpecification()).toLowerCase() + modelSpecificationInfo.getVendorId();
                if (specMap != null && specMap.containsKey(key)) {
                    sbf.append("规格型号已存在");
                    sbf.append(";");
                }
                if (!Strings.isNullOrEmpty(sbf.toString())) {
                    importParam.setErrorMsg(sbf.toString());
                    failList.add(importParam);
                    continue;
                }
                specList.add(modelSpecificationInfo);
            }
        }
        //**** 3:导入数据 ****
        if (CollectionUtils.isNotEmpty(specList)) {
            specService.doBatchAdd(specList);
        }
        log.info("{}条数据，导入失败数据！", failList.size());
        log.info("{}条数据，添加数据结束！", specList.size());
    }
}
